//
//  LXError.swift
//  LXMoyaHelper
//
//  Created by yoctech on 2021/8/5.
//

import Foundation
import Moya

public enum LXError: Error {
    case imageMapping(Response?)

    /// Indicates a response failed to map to a JSON structure.
    case jsonMapping(Response?)

    /// Indicates a response failed to map to a String.
    case stringMapping(Response?)

    /// Indicates a response failed to map to a Decodable object.
    case objectMapping(Error, Response?)

    /// Indicates that Encodable couldn't be encoded into Data
    case encodableMapping(Error)

    /// Indicates a response failed with an invalid HTTP status code.
    case statusCode(Response)

    /// Indicates a response failed due to an underlying `Error`.
    case underlying(Error, Response?)

    /// Indicates that an `Endpoint` failed to map to a `URLRequest`.
    case requestMapping(String)

    /// Indicates that an `Endpoint` failed to encode the parameters for the `URLRequest`.
    case parameterEncoding(Error)
    
    case responseCheck(Int, String?)
    
    case responseCheckOther(String, String?)
    
    case other(Int, message: String)
    

}

public extension LXError {
    var code: Int {
        switch self {
        case .imageMapping:
            return 70001
        case .jsonMapping:
            return 70002
        case .stringMapping:
            return 70003
        case .objectMapping(let err, _):
            return self.checkErrorCode(err: err)
        case .encodableMapping(let err):
            return self.checkErrorCode(err: err)
        case .statusCode(let resp):
            return resp.statusCode
        case .underlying(let err, _):
            return self.checkErrorCode(err: err)
        case .requestMapping(_):
            return 70005
        case .parameterEncoding(let err):
            return self.checkErrorCode(err: err)
        case .responseCheck(let code, _):
            return code
        case .responseCheckOther(let code, _):
            return Int(code) ?? 70006
        case .other(let code, _):
            return code
        }
    }
    
    var localizedDescription: String {
        switch self {
        case .imageMapping:
            return "图片解析失败"
        case .jsonMapping:
            return "JSON解析失败"
        case .stringMapping:
            return "String解析失败"
        case .objectMapping(let err, _):
            return err.localizedDescription
        case .encodableMapping(let err):
            return err.localizedDescription
        case .statusCode(let code):
            return "HTTP请求失败,错误码\(code)"
        case .underlying(let err, _):
            return err.localizedDescription
        case .requestMapping(let str):
            return str
        case .parameterEncoding(let err):
            return err.localizedDescription
        case .responseCheck(_, let msg):
            return msg ?? "服务器返回未知错误"
        case .responseCheckOther(_, let msg):
            return msg ?? "服务器返回未知错误"
        case .other(_, let msg):
            return msg
        }
    }

    
    var message: String {
        switch self {
        case .imageMapping:
            return "图片解析失败"
        case .jsonMapping:
            return "JSON解析失败"
        case .stringMapping:
            return "String解析失败"
        case .objectMapping(let err, _):
            return self.checkErrorDomain(err: err)
        case .encodableMapping(let err):
            return self.checkErrorDomain(err: err)
        case .statusCode(let code):
            return "HTTP请求失败,错误码\(code)"
        case .underlying(let err, _):
            return self.checkErrorDomain(err: err)
        case .requestMapping(let str):
            return str
        case .parameterEncoding(let err):
            return self.checkErrorDomain(err: err)
        case .responseCheck(_, let msg):
            return msg ?? "服务器返回未知错误"
        case .responseCheckOther(_, let msg):
            return msg ?? "服务器返回未知错误"
        case .other(_, let msg):
            return msg
        }
    }
    
    var underlyingError: Error? {
        switch self {
        case .imageMapping: return nil
        case .jsonMapping: return nil
        case .stringMapping: return nil
        case .objectMapping(let error, _):
            if let afError = error.asAFError {
                return afError.underlyingError
            }
            return error
        case .encodableMapping(let error):
            if let afError = error.asAFError {
                return afError.underlyingError
            }
            return error
        case .statusCode: return nil
        case .underlying(let error, _):
            if let afError = error.asAFError {
                return afError.underlyingError
            }
            return error
        case .requestMapping: return nil
        case .parameterEncoding(let error):
            if let afError = error.asAFError {
                return afError.underlyingError
            }
            return error
        case .responseCheck(_, _): return nil
        case .responseCheckOther(_, _): return nil
        case .other(_, message: _): return nil
        }
    }
    
    var response: Moya.Response? {
        switch self {
        case .imageMapping(let response): return response
        case .jsonMapping(let response): return response
        case .stringMapping(let response): return response
        case .objectMapping(_, let response): return response
        case .encodableMapping: return nil
        case .statusCode(let response): return response
        case .underlying(_, let response): return response
        case .requestMapping: return nil
        case .parameterEncoding: return nil
        case .responseCheck(_, _): return nil
        case .responseCheckOther(_, _): return nil
        case .other(_, message: _): return nil
        }
    }

    
    
    
    private func checkErrorDomain(err: Error) -> String {
        if let afError = err.asAFError, let nextErr = afError.underlyingError {
            return (nextErr as NSError).domain
        }
        return (err as NSError).domain
    }
    
    private func checkErrorCode(err: Error) -> Int {
        if let afError = err.asAFError, let nextErr = afError.underlyingError {
            return (nextErr as NSError).code
        }
        return (err as NSError).code
    }
}
